#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <signal.h>
#include <string>
#include <pthread.h>
#include <map>
#include <math.h>
#include <list>

#include "definitions.hpp"

using namespace std;

uint16_t mini, maxi;
int packets_read;
int random_number;
void generateDupBuffer();
map<int, bool> Seq_Map;
map<int, bool>::iterator sit;
uint16_t num_packets=0;
uint16_t chunk_id_ser = 0;

void *rdwr_thread_func(void*){
    
	pthread_mutex_lock(&synch_lock);
    pthread_cond_wait(&wakeup_udp,&synch_lock);
    pthread_mutex_unlock(&synch_lock);
    
	int fsize = fileSize;
    
	//cout<<"fisize "<<fsize<<endl;
    
    int cache_loop =  ceil((double)fsize/(double)(max_cache_size_c2 * max_cache_size_c1));
    double cache_count_1 = ceil(fsize/(double)(max_cache_size_c2));  // Tells the number of times the loop must execute
	int cache_count_2 = fsize%max_cache_size_c2;
	//unsigned char packet_buf[max_pkt_size];
	// curr loop tells us what is the loop number now. 
    int curr_loop = 0; 
    //    int f_remain;
    
	unsigned char* buf;
	uint16_t Seq_Number;
	
    
	//cout<<"Control in rdwr thread"<<endl;
    
	//cout<<"cache_loop"<<cache_loop<<endl;
	//cout<<"curr_loop"<<curr_loop<<endl;

	int curr_max_row = max_cache_size_c1;

    FILE* fp = fopen("Output","w");
    
	while(curr_loop<cache_loop){
		//cout<<"Inside currloop<cacheloop"<<endl;
        //cout<<"cache_loop-----------------"<<cache_loop<<endl;
        //cout<<"curr_loop----------------------"<<curr_loop<<endl;
        
        mini = 0;
        maxi = 0;
        
        random_number = 0;

		
        //Probability for the next Nack
        //Generating Random Number
           
        //CLEAR HASH
        Seq_Map.clear();
		packet_q.clear();
        
		 
		int curr_count;
        
        if(curr_loop == (cache_loop-1)){
//            num_packets = ((cache_count_1-1) *45) + ceil(cache_count_2/(double)max_load_size);
			num_packets = ((cache_count_1-1)*45)-(curr_loop*45*max_cache_size_c1) + ceil(cache_count_2/(double)max_load_size);
			curr_max_row = ceil((fsize - (curr_loop*max_cache_size_c2*max_cache_size_c1))/(double)max_cache_size_c2);
        }else{
            num_packets = 45*max_cache_size_c1;

        }
        
        //cout<<"num_packets---------------------------"<<num_packets<<endl;
        
        while(1){
            if(packet_q.size()==0){
                usleep(10);
                continue;
            }
            
            pthread_mutex_lock(&queue_lock);
            //bzero(buf,max_pkt_size);
            buf = packet_q.front();
            packet_q.pop_front();
            pthread_mutex_unlock(&queue_lock);
            
            uint16_t chunk_id_serv;
            memcpy(&Seq_Number,&buf[max_load_size],2);
			memcpy(&chunk_id_serv,&buf[max_load_size+2],2);
            //cout<<"seq_number "<<Seq_Number<<endl;
			//cout<<"chunkid "<<chunk_id_serv<<endl;
			//cout<<"chunkid ................"<<chunk_id_ser<<endl;
			//cout<<"curr_loop "<<curr_loop<<endl;


			if(chunk_id_serv != curr_loop){
				//usleep(100);

				free(buf);
				continue;
			}
             curr_count++;        
            
            int quot = Seq_Number/45;
            int rem = Seq_Number%45;
            int row = 0;
            int col = 0;
            if(rem == 0){
                row = quot - 1;
                col = 44;
            }else{
                row = quot;
                col = rem-1;
            }
            
            memcpy(&cache[row][col*max_load_size],&buf[0],max_load_size);   
            free(buf);
            
            //hash seq_number

            Seq_Map[Seq_Number] = 1;
			/*cout<<"Mini Value"<<mini<<endl;
            cout<<"Maxi Value"<<maxi<<endl;
                   
            if(Seq_Number == (mini+1)){
			
                if(Seq_Number > maxi){
                    maxi = Seq_Number;
                }
                mini = Seq_Number;
                
                for(uint16_t y = (mini+1);y<=maxi;y++){
					//cout<<"checking for y"<<y<<endl;
                    sit = Seq_Map.find(y);
                    if(sit == Seq_Map.end()){
                        mini = y-1; 
						updated = 1;
                        cout<<"setting mini to "<<mini<<endl;
						break;
                    }
                }

				if(updated == 0) {
                   cout<<"setting mini to maxi "<<maxi<<endl;
                   mini = maxi;
				}
               // mini = Seq_Number;
            }else if(Seq_Number > maxi){
                // Push into Hash Map // a[max] = true;
                maxi = Seq_Number;
            }else ;*/
              uint16_t y;
              for(y = 1;y<=num_packets;y++){
					//cout<<"checking for y"<<y<<endl;
                    sit = Seq_Map.find(y);
                    if(sit == Seq_Map.end()){
                       // mini = y-1; 
						//updated = 1;
                        //cout<<"setting mini to "<<mini<<endl;
						break;
                    }					
             }
             
			 if(y==(num_packets+1) &&  num_packets != 0){
                updated = 0;
			   // ack_flag = 1;
			   //cout<<"setting hwat u want"<<endl;
			   num_packets = 0;
					  //break;
		     }
            
            if(updated == 0){

			  pthread_mutex_lock(&sleep_lock);
		      pthread_cond_signal(&sleep_cond);
              // pthread_cond_signal(&sleep_cond);
             pthread_mutex_unlock(&sleep_lock);

				//cout<<"num_packets ..........................+++++++++++++++++++++" <<num_packets<<endl;
                
                // Signal TCP saying Cache Full
                // write case into file 
//                FILE* fp = fopen("Output","w");
                
                //cout<<"cache_count_1 "<<cache_count_1<<endl;
                //cout<<"cache_count_2 "<<cache_count_2<<endl;
                
                for(int it = 0;it<curr_max_row;it++){
                    
                    if((it == curr_max_row - 1) && (curr_loop == (cache_loop-1))){ 
                        
                        fwrite(&cache[it][0], cache_count_2 , 1 , fp);
                    }else{
                        fwrite(&cache[it][0], max_cache_size_c2 , 1 , fp);
                    }
                }
                  //usleep(100000);
//                fclose(fp);

                pthread_mutex_lock(&sleep_lock);
		        pthread_cond_wait(&sleep_cond,&sleep_lock);
          // pthread_cond_signal(&sleep_cond);
                pthread_mutex_unlock(&sleep_lock);
                break;
            } // if min == num_packets
            
        }
         //cout<<"Recieved a Cache"<<endl;
		 //cout<<"packet count is "<<curr_count<<endl;
         
         curr_loop++;
		 chunk_id_ser++;
        
    }
    
    
    fclose(fp);  
    file_complete_flag = 1;
    //cout<<"DIE UBEACHRD_WR"<<endl;
    pthread_exit(NULL);
}


void *sleep_thread_func(void*){
    int sleep_sigflag =0;
	int start = 0;
	uint16_t k = 0;

	while(file_complete_flag != 1){
        
        srand(0);
        random_number = 100000;//(rand()%100)+15;
       // sleep_sigflag =0;
        
        usleep(random_number);

        int pt = 0 ;
		//unsigned char * dupBuffer = new unsigned char[2500];
		//cout<<"camee to send NACL" <<endl;
		//uint16_t bound;
		//bound = num_packets;
		//cout<<"num_packets +++++++++++++++++++++" <<num_packets<<endl;
		/*if(num_packets - mini < 100){
			bound = num_packets;
		}
		else{
			bound = maxi;
		}*/
		
        for(uint16_t y = 1;y<=num_packets;y++)
		{
            sit = Seq_Map.find(y);
            if(sit == Seq_Map.end()){				
				//cout<<"adding k" <<k<<endl;
				//updated = 1;
                memcpy(&dupBuffer[pt],&y,2);
                pt = pt+2;		   
				/*if(pt >= 100){
			      break;
				}*/
            }			
        }

    }

    pthread_mutex_lock(&sleep_lock);
    pthread_cond_signal(&sleep_cond);
    pthread_mutex_unlock(&sleep_lock);
    //cout<<"Exiting sleepfucking thread"<<endl;
    pthread_exit(NULL);
}


